/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package swym.server;

import swym.protocol.*;

import java.net.*;
import java.io.*;
import java.util.LinkedList;

/**
 *
 * @author mschaefer
 */
public class SocketOutputThread extends Thread
{
    private static SocketOutputThread instance = new SocketOutputThread();
    
    public static SocketOutputThread getInstance() { return instance; }
    
    private LinkedList<Action> actionQueue;
    
    private volatile boolean stopThread;
    private Object wakeUpObject;
    
    /**
     * Instantiates the thread (but doesn't start it)
     */
    private SocketOutputThread()
    {
        stopThread = false;
        
        wakeUpObject = new Object();
        
        actionQueue = new LinkedList<Action>();
    }
    
    /**
     * Add an action to the queue to be sent to the specified socket.
     * 
     * @param connection
     * @param socket
     * @param message
     */
    public void queueAction( ClientConnection connection, ClientSocket socket, ActionMessage message )
    {
        Action action = new Action( connection, socket, message );
        
        synchronized ( actionQueue )
        {
            actionQueue.add( action );
        }
        
        wakeUpThread();
    }
    
    public void startThread()
    {
        stopThread = false;
        
        this.start();
    }
    
    public void stopThread()
    {
        stopThread = true;
        wakeUpThread();
        
        try
        {
            this.join();
        }
        catch ( Exception e ) {}
    }
    
    /**
     * main thread loop; if there aren't any items
     * in the queue, then wait. Once awoken, check
     * again for items in the queue.
     */
    public void run()
    {
        Action action = null;
        
        while ( !stopThread )
        {
            if ( !isActionQueueEmpty() )
            {
                action = getNextAction();
                
                ObjectOutputStream out = action.socket.getOutputStream();
                
                try
                {
                    out.writeObject( action.message );
                }
                catch ( Exception e )
                {
                    // if we failed to send the item, then ....
                }
            }
                       
            synchronized( wakeUpObject )
            {
                try
                {
                    wakeUpObject.wait();
                }
                catch ( Exception e ) {}
            }
        }
    }
    
    /**
     * Helper for the thread loop
     * @return
     */
    private boolean isActionQueueEmpty()
    {
        synchronized ( actionQueue )
        {
            return actionQueue.isEmpty();
        }
    }
    
    /**
     * Removes and returns first (next) item 
     * in the queue. (Helper for main thread
     * loop).
     * @return
     */
    private Action getNextAction()
    {
        synchronized ( actionQueue )
        {
            return actionQueue.removeFirst();
        }
    }
    
    /**
     * 
     */
    private void wakeUpThread()
    {
        synchronized ( wakeUpObject )
        {
           wakeUpObject.notify(); 
        }
    }
    
    /**
     * Essentially a data structure for a queued item
     */
    private class Action
    {
        public ClientConnection connection;
        public ClientSocket socket;
        public ActionMessage message;
        
        public Action( ClientConnection connection, ClientSocket socket, ActionMessage message )
        {
            this.connection = connection;
            this.socket = socket;
            this.message = message;
        }
    }
}
